除了上述安裝、解除安裝、在不同 Scope 切換不同的 Python 版本以外,以下還有一些比較特別的使用方法
pyenv shell
在 shell 配置檔末加入 pyenv init -
將可以自動載入 pyenv
,接著才能夠使用 pyenv shell
pyenv shell <verison>
將可以設定 PYENV_VERSION
環境變數,作為 shell 使用的 Python 版本,此版本將會覆蓋全域及區域的 Python 版本,若不需要可以使用 pyenv shell --unset
取消。
當想要在 shell 中使用多個版本的 Python 的話,可以輸入 pyenv shell <version>...
,輸入後便可以在環境中使用 Python 的版本,舉例來說,想要可以使用 2.7.7 和 3.8.0 的話可以輸入 pyenv shell 2.7.6 3.8.0
,接著可以看到下面的結果:
$ pyenv versions
system
* 2.7.6 (set by PYENV_VERSION environment variable)
* 3.3.3 (set by PYENV_VERSION environment variable)
$ python --version
Python 2.7.6
$ python2.7 --version
Python 2.7.6
$ python3.3 --version
Python 3.3.3
輸入比較前面的版號將會為是優先使用的 Python 版本,所以指令 python 會使用 2.7.6
另外,pyenv global
和 pyenv local
同樣接受多個版本號的參數,作用如同 pyenv shell
輸入多個版本號,差別在於 pyenv shell
版本將會覆蓋 pyenv local
版本,並且 pyenv local
版本會覆蓋 pyenv global
版本。
pyenv rehash
pyenv rehash
是在每次下載新的 Pyton 版本時,用於更新 Shims 可以使用的指令,其實在 pyenv init
和 pyenv install
中都會在執行這個指令,供使用者方便使用。
如同前面介紹 pyenv init
時提及的, pyenv
將會修改 PATH
這個環境變數,要了解 Pyenv 的運作怎麼切換不同版本的 Python 首先要先了解 PATH
環境變數,以及 Shims 在 Pyenv 中扮演的角色為何。
PATH
環境變數當想要在 shell 中執行任何指令時,系統首先要知道這些指令是什麼,然而系統便會去一個個的路徑尋找相同名字的可執行檔案,而這些路徑將會首先定義在 PATH
環境變數中,若在 shell 中執行 echo $PATH
將可以看到一串由冒號分隔的字串,例如:
/Users/xxx/.pyenv/shims:/usr/local/opt/llvm/bin:/Library/Frameworks/Python.framework/Versions/3.5/bin:/opt/local/bin/:/Users/xxx/bin
系統將會由左至右開始查找,因此在前面的目錄先找到的話便不會往下繼續找,而當輸入 eval "$(pyenv init -)"
時會將把 ${PYENV_ROOT}/shims
加入 PATH
的最前面,因此達到呼叫 Pyenv shims
中的指令而非系統的。
Shim 在維基百科的解釋是:
In computer programming, a shim is a library that transparently intercepts API calls and changes the arguments passed, handles the operation itself or redirects the operation elsewhere.
大意指的是 Shim 的主要工作就是擷取 API 呼叫並且改變其中的參數,隨後將改變後的參數傳給其他執行單元執行、或自身處理。
而在 ${PYENV_ROOT}/shims
中的每支腳本都是做這樣的事情(Pyenv 稱之為 rehashing),其中的程式碼如下:
#!/usr/bin/env bash
set -e
[ -n "$PYENV_DEBUG" ] && set -x
program="${0##*/}"
if [[ "$program" = "python"* ]]; then
for arg; do
case "$arg" in
-c* | -- ) break ;;
*/* )
if [ -f "$arg" ]; then
export PYENV_FILE_ARG="$arg"
break
fi
;;
esac
done
fi
export PYENV_ROOT="/Users/wilson/.pyenv"
exec "/usr/local/Cellar/pyenv/1.2.20/libexec/pyenv" exec "$program" "$@"
其中可以看到最後一行會將輸入的指令及參數帶入至 pyenv exec
執行,這些程式碼也是在 pyenv rehash
時建立於 ${PYENV_ROOT}/shims
下的。
pyenv exec
在做什麼查看原始碼可以發現,下面者一段:
PYENV_COMMAND_PATH="$(pyenv-which "$PYENV_COMMAND")"
PYENV_BIN_PATH="${PYENV_COMMAND_PATH%/*}"
# ...
if [ "${PYENV_BIN_PATH#${PYENV_ROOT}}" != "${PYENV_BIN_PATH}" ]; then
# Only add to $PATH for non-system version.
export PATH="${PYENV_BIN_PATH}:${PATH}"
fi
exec "$PYENV_COMMAND_PATH" "$@"
其中透過呼叫 pyenv which
可以得出當前使用的 Python 版本,並且取得其 bin/
位置加入至 PATH
環境變數,最後再帶入原先帶入的參數執行。
.python-version
, version
, PYTHON_VERSION
這樣的檔案或變數在,也可以透過 pyenv 中的 python-build 來幫助自己下載 特定的 Python 版本並解壓縮、編譯到想要的位置。$ python-build 3.6.5 ~/.local/pythons/3.6
$ python-build 3.5.4 ~/.local/pythons/3.5
$ ln -s ~/.local/pythons/3.6/python3.6 ~/.local/bin
$ ln -s ~/.local/pythons/3.5/python3.5 ~/.local/bin
$ ln -s ~/.local/bin/python3.6 ~/.local/bin/python3
python() {
local PYTHON="$(which python)"
if [[ "$PYTHON" == /usr/* ]];
then
echo "nope" >&2 | echo >/dev/null
else
"$PYTHON" "$@"
fi
}
如此便可以避免呼叫到系統的 Python 版本。